﻿using BunnyEater.Common.Jwt.Dto;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace BunnyEater.Common.Jwt
{
    /// <summary>
    ///  iss(Issuser)：JWT的签发主体；
    ///  sub(Subject)：JWT的主体，即它的所有人
    ///  aud(Audience)：JWT的接收对象
    ///  exp(Expiration time)：时间戳，JWT的过期时间
    ///  nbf(Not Before)：时间戳，JWT生效的开始时间
    ///  iat(Issued at)：时间戳，JWT的签发时间
    ///  jti(JWT ID)：JWT的唯一标识
    ///   </summary>
    public class JwtHelper
    {

        #region 方式一：生成JWT
        /// <summary>
        /// 创建用户标识
        /// </summary>
        /// <param name="tokenType">token类型</param>
        /// <param name="user"></param>
        /// <returns></returns>
        public static Claim[] CreateClaims(TokenType tokenType, UserDto user)
        {
            var second = tokenType == TokenType.AccountToken ? 100 : 200;
            var claims = new Claim[]
            {
                // 禁止存放敏感数据 ！！如：手机号、银行卡、身份证号、密码等
                //使用JWT的ClaimTypes存储用户信息
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 用户id
                new Claim(ClaimTypes.Name, user.Account), // 用户名
                //自己设置键值对存储用户信息
                new Claim("userId", user.Id.ToString()), // 用户id
                new Claim("account", user.Account), // 用户名
                //new Claim("admin", user.IsAdmin.ToString()), // 是否是管理员
                new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),//用户id做主体
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),//jwt唯一标识
                new Claim(JwtRegisteredClaimNames.Iss,JwtConfig.JwtInfo.Issuer),//颁发者：项目API名
                new Claim(JwtRegisteredClaimNames.Aud,JwtConfig.JwtInfo.Audience),//接收者：运行起来的项目WebApp
                new Claim(JwtRegisteredClaimNames.Iat,$"{ DateTimeOffset.Now.ToUnixTimeSeconds()}") ,//签发时间；
                new Claim(JwtRegisteredClaimNames.Nbf,$"{DateTimeOffset.Now.ToUnixTimeSeconds()}") ,//生效开始时间
                new Claim(JwtRegisteredClaimNames.Exp,$"{ DateTimeOffset.Now.AddMinutes(JwtConfig.JwtInfo.ExpiryMinutes).ToUnixTimeSeconds()}")//过期时间:一小时(AddMinutes(分钟))
                //new Claim(JwtRegisteredClaimNames.Exp,$"{ DateTimeOffset.Now.AddDays(7).ToUnixTimeSeconds()}")//过期时间:7天(AddDays(天数))
                //new Claim(JwtRegisteredClaimNames.Exp,$"{ DateTimeOffset.Now.AddSeconds(second).ToUnixTimeSeconds()}")//过期时间：second秒(AddSeconds(秒数))
            };

            ////DateTime,表示时间上的一刻，通常以日期和当天时间来表示。
            ////DateTimeOffset,表示一个时间点，通常以相对于协调世界时（UTC）的日期和时间来表示
            //var t3 = DateTime.Now;//本地时间
            //var t1 = DateTimeOffset.Now;//+8
            //var t11 = DateTimeOffset.UtcNow;//-8
            //var t2 = new DateTimeOffset(DateTime.Now);//+8
            //var t4 = DateTime.UtcNow;//-8
            //var t41 = DateTimeOffset.Now.ToUnixTimeSeconds();

            return claims;
        }

        /// <summary>
        /// 创建token
        /// </summary>
        /// <param name="claims"></param>
        /// <returns></returns>
        public static string GenerateToken(TokenType tokenType, Claim[] claims)
        {
            //秘钥长度必须：>=16位
            var securityKey = tokenType == TokenType.AccountToken
                ? JwtSecret.GetAccountKey() : JwtSecret.GetRefreshKey();

            //var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(securityKey));//取出秘钥，对称安全键
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);//签名凭据，安全算法
            var token = new JwtSecurityToken(
              issuer: JwtConfig.JwtInfo.Issuer,
              audience: JwtConfig.JwtInfo.Audience,
              signingCredentials: creds,//签名凭据
              claims: claims,
              expires: DateTime.Now.AddMinutes(Convert.ToDouble(JwtConfig.JwtInfo.ExpiryMinutes))
            );
            var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
            return jwtToken;
        }

        #endregion

        /// <summary>
        /// 方式二： 创建Token
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public string GetNewToken(UserDto user)
        {
            //创建用户身份标识  --声明 
            var claims = new Claim[]
            {
                new Claim("id", user.Id.ToString()), // 用户id
                new Claim("name", user.Account), // 用户名
                //new Claim("admin", user.IsAdmin.ToString()), // 是否是管理员

                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),//唯一标识
                new Claim(JwtRegisteredClaimNames.Iss,JwtConfig.JwtInfo.Issuer),//颁发者
                new Claim(JwtRegisteredClaimNames.Aud,JwtConfig.JwtInfo.Audience),//接收者
                new Claim(JwtRegisteredClaimNames.Iat,$"{ DateTimeOffset.Now.ToUnixTimeSeconds()}") ,//签发时间
                new Claim(JwtRegisteredClaimNames.Nbf,$"{DateTimeOffset.Now.ToUnixTimeSeconds()}") ,//生效开始时间
                new Claim(JwtRegisteredClaimNames.Exp,$"{ DateTimeOffset.Now.AddDays(10).ToUnixTimeSeconds()}")//过期时间
            };

            //创建令牌
            //var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(JwtSecret.GetAccountKey()));//取出秘钥，对称安全键
            var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("123456789123456789"));//取出秘钥，对称安全键
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);//签名凭据，安全算法  不可逆

            //载荷+安全算法+私钥
            var securityToken = new JwtSecurityToken(signingCredentials: creds, claims: claims);

            string jwtToken = new JwtSecurityTokenHandler().WriteToken(securityToken);
            return jwtToken;
        }

        /// <summary>
        /// 创建解码器
        /// </summary>
        /// <returns></returns>
        public static IJwtDecoder GetJwtDecoder()
        {
            IJsonSerializer serializer = new JsonNetSerializer();
            IDateTimeProvider provider = new UtcDateTimeProvider();
            IJwtValidator validator = new JwtValidator(serializer, provider);
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
            IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
            return decoder;
        }

        /// <summary>
        /// 此方法用解码字符串token，并返回秘钥的信息对象
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        public static ClaimsPrincipal GetPrincipal(string token)
        {
            try
            {
                var tokenHandler = new JwtSecurityTokenHandler(); // 创建一个JwtSecurityTokenHandler类，用来后续操作
                var jwtToken = tokenHandler.ReadToken(token) as JwtSecurityToken; // 将字符串token解码成token对象
                if (jwtToken == null)
                    return null;
                var symmetricKey = Convert.FromBase64String(JwtSecret.GetAccountKey()); // 生成编码对应的字节数组
                var validationParameters = new TokenValidationParameters() // 生成验证token的参数
                {
                    RequireExpirationTime = true, // token是否包含有效期
                    ValidateIssuer = false, // 验证秘钥发行人，如果要验证在这里指定发行人字符串即可
                    ValidateAudience = false, // 验证秘钥的接受人，如果要验证在这里提供接收人字符串即可
                    IssuerSigningKey = new SymmetricSecurityKey(symmetricKey) // 生成token时的安全秘钥
                };
                SecurityToken securityToken; // 接受解码后的token对象
                var principal = tokenHandler.ValidateToken(token, validationParameters, out securityToken);
                return principal; // 返回秘钥的主体对象，包含秘钥的所有相关信息
            }

            catch (Exception ex)
            {
                return null;
            }
        }

    }
}
